//-------------------------------------------------------------------
//	Copyright (c) 2010-2012  Zhirnov Andrey
//	This file is part of the "UXGEN" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#include "MeshLoader.h"

namespace Engine
{
namespace Loaders
{

	bool LoadMesh(IFilePtr pFile, TMesh &sMesh, e_model_component::type &eType);
	bool LoadAnimation(IFilePtr pFile, TMeshAnimation &sAnim, e_model_component::type &eType);
	bool LoadMaterial(IFilePtr pFile, TMeshMaterial &sMaterial, e_model_component::type &eType);
	bool LoadPhysics(IFilePtr pFile, TMeshPhysics &sPhysics, e_model_component::type &eType);

	bool LoadSingleMesh(IFilePtr pFile, TMultiMesh &sMesh);
	bool LoadMultiMesh(IFilePtr pFile, TMultiMesh &sMesh);
	bool LoadSceletalMesh(IFilePtr pFile, TSceletalMesh &sMesh);

	bool LoadFrameAnim(IFilePtr pFile, TFrameAnimData &sAnim);
	bool LoadSceletalAnim(IFilePtr pFile, TSceletalAnimData &sAnim);

	bool LoadMaterialsList(IFilePtr pFile, TMeshMaterialsList &sMtrs);


	bool SaveMesh(IFilePtr pFile, const TMesh &sMesh);
	bool SaveAnimation(IFilePtr pFile, const TMeshAnimation &sAnim);
	bool SaveMaterial(IFilePtr pFile, const TMeshMaterial &sMaterial);
	bool SavePhysics(IFilePtr pFile, const TMeshPhysics &sPhysics);

	bool SaveSingleMesh(IFilePtr pFile, const TMultiMesh &sMesh);
	bool SaveMultiMesh(IFilePtr pFile, const TMultiMesh &sMesh);
	bool SaveSceletalMesh(IFilePtr pFile, const TSceletalMesh &sMesh);
	
	bool SaveFrameAnim(IFilePtr pFile, const TFrameAnimData &sAnim);
	bool SaveSceletalAnim(IFilePtr pFile, const TSceletalAnimData &sAnim);
	
	bool SaveMaterialsList(IFilePtr pFile, const TMeshMaterialsList &sMtrs);



	inline gl_index::type GetIndexType(uint8 uIdxSize)
	{
		switch ( uIdxSize )
		{
			case sizeof(uint8) :
				return gl_index::UBYTE;

			case sizeof(uint16) :
				return gl_index::USHORT;

			case sizeof(uint32) :
				return gl_index::UINT;

			default :
				WARNING( "unknown index type whis size" );
				return gl_index::_UNKNOWN;
		};
	}

	inline uint8 GetIndexSize(gl_index::type eType)
	{
		switch ( eType )
		{
			case gl_index::UBYTE :
				return sizeof(uint8);

			case gl_index::USHORT :
				return sizeof(uint16);

			case gl_index::UINT :
				return sizeof(uint32);

			default :
				WARNING( "unknown index size for type" );
				return 0;
		};
	}

	inline gl_primitive::type GetPrimitiveType(GLenum eType)
	{
		return (gl_primitive::type)eType;
	}

	inline GLenum GetPrimitiveMode(gl_primitive::type eType)
	{
		return (GLenum)eType;
	}


	
	bool LoadModel(IFilePtr pFile, TMesh *pMesh, TMeshAnimation *pAnimation, TMeshMaterial *pMaterial, TMeshPhysics *pPhysics, e_model_component::type *pType)
	{
		CHECK_ARGUMENT( pFile != nullptr );
		CHECK_ARGUMENT( pMesh != nullptr || pAnimation != nullptr ||
						pMaterial != nullptr || pPhysics != nullptr );

		char					a_file_header[12];
		TModelHeader			s_header;
		TFileHeader				s_file_header;
		uint					u_pos	= pFile->GetPos();
		e_model_component::type	e_type	= e_model_component::UNKNOWN;
		bool					b_read	= true;


		b_read &= pFile->ReadS( a_file_header, sizeof(a_file_header) );
		a_file_header[11] = '\0';

		CHECK_VALUE( strcmp( a_file_header, s_aMdlFileHeader ) == 0 );
		
		b_read &= pFile->ReadS( &s_file_header, sizeof(s_file_header) );
		b_read &= pFile->SeekS( s_file_header.uUserDataSize, e_seek::CURRENT );
		b_read &= pFile->ReadS( &s_header, sizeof(s_header) );

		CHECK_VALUE( s_file_header.uHeaderSize == sizeof(s_header) );

		if ( s_header.uGeometryOffset != 0 && pMesh != nullptr )
		{
			b_read &= pFile->SeekS( u_pos + s_header.uGeometryOffset, e_seek::SET );
			b_read &= LoadMesh( pFile, *pMesh, e_type );
		}

		if ( s_header.uAnimationOffset != 0 && pAnimation != nullptr )
		{
			b_read &= pFile->SeekS( u_pos + s_header.uAnimationOffset, e_seek::SET );
			b_read &= LoadAnimation( pFile, *pAnimation, e_type ); 
		}

		if ( s_header.uMaterialOffset != 0 && pMaterial != nullptr )
		{
			b_read &= pFile->SeekS( u_pos + s_header.uMaterialOffset, e_seek::SET );
			b_read &= LoadMaterial( pFile, *pMaterial, e_type );
		}

		if ( s_header.uPhysicsOffset != 0 && pPhysics != nullptr )
		{
			b_read &= pFile->SeekS( u_pos + s_header.uPhysicsOffset, e_seek::SET );
			b_read &= LoadPhysics( pFile, *pPhysics, e_type );
		}

		if ( pType != nullptr )
			*pType = e_type;

		return b_read;
	}

	
	bool LoadMesh(IFilePtr pFile, TMesh &sMesh, e_model_component::type &eType)
	{
		char	a_mesh_type[4];
		bool	b_read = true;

		b_read &= pFile->ReadS( a_mesh_type, sizeof(a_mesh_type) );
		a_mesh_type[3] = '\0';

		if ( strcmp( a_mesh_type, "SMH" ) == 0 )
		{
			b_read &= sMesh.Create( TMesh::MULTI_MESH );
			b_read &= LoadSingleMesh( pFile, *sMesh.sData.pMesh );
			eType  |= e_model_component::MESH;
		}
		else
		
		if ( strcmp( a_mesh_type, "MMH" ) == 0 )
		{
			b_read &= sMesh.Create( TMesh::MULTI_MESH );
			b_read &= LoadMultiMesh( pFile, *sMesh.sData.pMesh );
			eType  |= e_model_component::MULTI_MESH;
		}

		else
		if ( strcmp( a_mesh_type, "SCM" ) == 0 )
		{
			b_read &= sMesh.Create( TMesh::SCELETAL_MESH );
			b_read &= LoadSceletalMesh( pFile, *sMesh.sData.pScelet );
			eType |= e_model_component::SCELETAL_MULTI_MESH;
		}
		
		else
			b_read = false;

		return b_read;
	}
	

	bool LoadSingleMesh(IFilePtr pFile, TMultiMesh &sMesh)
	{
		TSMeshHeader	s_mesh_header;
		bool			b_read = true;
		
		b_read &= pFile->ReadS( &s_mesh_header, sizeof(s_mesh_header) );
		
		s_mesh_header.aName[31] = '\0';

		sMesh.aSubMeshes.Resize( 1, false );
		sMesh.aSubMeshes.Back().aAttribs.Resize( s_mesh_header.uVertAttribCount, false );
		sMesh.aVertices.Resize( s_mesh_header.uVerticesSize, false );
		sMesh.aIndices.Resize( s_mesh_header.uIndicesSize, false );
		sMesh.aSubMeshes.Back().eMode		= GetPrimitiveType( s_mesh_header.eMode );
		sMesh.aSubMeshes.Back().eType		= GetIndexType( s_mesh_header.uIdxSize );
		sMesh.aSubMeshes.Back().sBBox		= s_mesh_header.sAABB;
		sMesh.aSubMeshes.Back().sName		= s_mesh_header.aName;
		sMesh.aSubMeshes.Back().uIdxCount	= s_mesh_header.uIndicesSize / s_mesh_header.uIdxSize;

		sMesh.aIndices.PushBack( 0 );
		sMesh.aLODs.PushBack( 0 );

		b_read &= pFile->ReadS( sMesh.aSubMeshes.Back().aAttribs.ptr(), sMesh.aSubMeshes.Back().aAttribs.Size() );
		b_read &= pFile->ReadS( sMesh.aIndices.ptr(),	sMesh.aIndices.Size() );
		b_read &= pFile->ReadS( sMesh.aVertices.ptr(),	sMesh.aVertices.Size() );
		
		return b_read;
	}
	
	
	bool LoadMultiMesh(IFilePtr pFile, TMultiMesh &sMesh)
	{
		TMultiMeshHeader	s_mm_header;
		bool				b_read = true;

		b_read &= pFile->ReadS( &s_mm_header, sizeof(s_mm_header) );

		sMesh.aSubMeshes.Resize( s_mm_header.uSubMeshesCount, false );
		sMesh.aSMIndices.Resize( s_mm_header.uIndicesCount, false );
		sMesh.aVertices.Resize( s_mm_header.uVerticesSize, false );
		sMesh.aIndices.Resize( s_mm_header.uIndicesSize, false );
		sMesh.aLODs.Resize( s_mm_header.uLODsCount, false );

		for (uint i = 0; i < s_mm_header.uSubMeshesCount; ++i)
		{
			TSubMeshHeader		s_sm_header;
			TMultiMesh::TSubMesh &s_sub_mesh = sMesh.aSubMeshes[i];

			b_read &= pFile->ReadS( &s_sm_header, sizeof(s_sm_header) );
			s_sm_header.aName[31] = '\0';

			s_sub_mesh.aAttribs.Resize( s_sm_header.uAttribsCount, false );
			s_sub_mesh.eMode		= GetPrimitiveType( s_sm_header.ePrimitiveMode );
			s_sub_mesh.eType		= GetIndexType( s_sm_header.uIdxSize );
			s_sub_mesh.sBBox		= s_sm_header.sAABB;
			s_sub_mesh.sName		= s_sm_header.aName;
			s_sub_mesh.uIdxCount	= s_sm_header.uIdxCount;
			s_sub_mesh.uIdxOffset	= s_sm_header.uIdxOffset;

			b_read &= pFile->ReadS( s_sub_mesh.aAttribs.ptr(), s_sub_mesh.aAttribs.Size() );
		}

		b_read &= pFile->ReadS( sMesh.aIndices.ptr(),	sMesh.aIndices.Size() );
		b_read &= pFile->ReadS( sMesh.aVertices.ptr(),	sMesh.aVertices.Size() );
		b_read &= pFile->ReadS( sMesh.aSMIndices.ptr(),	sMesh.aSMIndices.Size() );
		b_read &= pFile->ReadS( sMesh.aLODs.ptr(),		sMesh.aLODs.Size() );

		return b_read;
	}


	bool LoadSceletalMesh(IFilePtr pFile, TSceletalMesh &sMesh)
	{
		TScelMultiMeshHeader	s_header;
		bool					b_read = true;

		b_read &= pFile->ReadS( &s_header, sizeof(s_header) );

		sMesh.aIndices.Resize( s_header.uIndicesSize, false );
		sMesh.aVertices.Resize( s_header.uVerticesSize, false );
		sMesh.aWeights.Resize( s_header.uWeightsCount, false );
		sMesh.aSubMeshes.Resize( s_header.uSubMeshesCount, false );
		sMesh.aSMIndices.Resize( s_header.uIndicesCount, false );
		sMesh.aLODs.Resize( s_header.uLODsCount, false );
		sMesh.uJointsPerFrame	= s_header.uJointsPerFrame;

		for (uint i = 0; i < sMesh.aSubMeshes.Count(); ++i)
		{
			TScelSubMeshHeader			s_sm_header;
			TSceletalMesh::TSubMesh	&	s_sub_mesh = sMesh.aSubMeshes[i];

			b_read &= pFile->ReadS( &s_sm_header, sizeof(s_sm_header) );

			s_sub_mesh.aAttribs.Resize( s_sm_header.uAttribsCount, false );
			s_sub_mesh.eMode		= GetPrimitiveType( s_sm_header.ePrimitiveMode );
			s_sub_mesh.eType		= GetIndexType( s_sm_header.uIdxSize );
			s_sub_mesh.uIdxCount	= s_sm_header.uIdxCount;
			s_sub_mesh.uIdxOffset	= s_sm_header.uIdxOffset;

			b_read &= pFile->ReadS( s_sub_mesh.aAttribs.ptr(), s_sub_mesh.aAttribs.Size() );
		}

		b_read &= pFile->ReadS( sMesh.aWeights.ptr(),	sMesh.aWeights.Size() );
		b_read &= pFile->ReadS( sMesh.aIndices.ptr(),	sMesh.aIndices.Size() );
		b_read &= pFile->ReadS( sMesh.aVertices.ptr(),	sMesh.aVertices.Size() );
		b_read &= pFile->ReadS( sMesh.aSMIndices.ptr(),	sMesh.aSMIndices.Size() );
		b_read &= pFile->ReadS( sMesh.aLODs.ptr(),		sMesh.aLODs.Size() );

		return b_read;
	}


	bool LoadAnimation(IFilePtr pFile, TMeshAnimation &sAnim, e_model_component::type &eType)
	{
		char	a_anim_type[4];
		bool	b_read = true;

		b_read &= pFile->ReadS( a_anim_type, sizeof(a_anim_type) );
		a_anim_type[3] = '\0';

		if ( strcmp( a_anim_type, "FRM" ) == 0 )
		{
			b_read &= sAnim.Create( TMeshAnimation::FRAME );
			b_read &= LoadFrameAnim( pFile, *sAnim.sData.pFrameAnim );
			eType  |= e_model_component::FRAME_ANIM;
		}
		else
		
		if ( strcmp( a_anim_type, "SCL" ) == 0 )
		{
			b_read &= sAnim.Create( TMeshAnimation::SCELETAL );
			b_read &= LoadSceletalAnim( pFile, *sAnim.sData.pSceletal );
			eType  |= e_model_component::SCELETAL_ANIM;
		}
		
		else
			b_read = false;

		return b_read;
	}
	

	bool LoadFrameAnim(IFilePtr pFile, TFrameAnimData &sAnim)
	{
		TFrameAnimHeader	s_header;
		bool				b_read = true;

		b_read &= pFile->ReadS( &s_header, sizeof(s_header) );

		sAnim.aTracks.Resize( s_header.uTracksCount, false );
		sAnim.aTrackFrames.Resize( s_header.uFramesCount, false );
		sAnim.fFrameRate = s_header.fFrameRate;

		b_read &= pFile->ReadS( sAnim.aTrackFrames.ptr(),	sAnim.aTrackFrames.Size() );

		for (usize i = 0; sAnim.aTracks.Count(); ++i)
		{
			TFrameAnimTrack				s_track;
			TFrameAnimData::TTrackInfo&	s_track_info = sAnim.aTracks[i];

			b_read &= pFile->ReadS( &s_track, sizeof(s_track) );

			s_track_info.sName		 = s_track.aName;
			s_track_info.uCount		 = s_track.uCount;
			s_track_info.uFirstFrame = s_track.uFirstFrame;
			s_track_info.uTrackType	 = s_track.uTrackType;
		}

		return b_read;
	}


	bool LoadSceletalAnim(IFilePtr pFile, TSceletalAnimData &sAnim)
	{
		typedef array< TSAJointInfo, TFastCopyStrategy< TSAJointInfo > >	joint_info_array_t;
		typedef array< TSATrackInfo, TFastCopyStrategy< TSATrackInfo > >	track_anim_array_t;

		TSceletalAnimHeader		s_header;
		bool					b_read = true;
		joint_info_array_t		a_joint_infos;
		track_anim_array_t		a_track_anims;


		b_read &= pFile->ReadS( &s_header, sizeof(s_header) );
		
		sAnim.aTrackFrames.Resize( s_header.uFramesCount, false );
		sAnim.aJoints.Resize( s_header.uFramesCount * s_header.uJointPerFrame, false );
		sAnim.aBBoxes.Resize( s_header.uBBoxCount,  false );
		sAnim.aTracks.Resize( s_header.uTracksCount, false );
		sAnim.aJointInfos.Resize( s_header.uJointPerFrame, false );
		a_joint_infos.Resize( s_header.uJointPerFrame, false );
		a_track_anims.Resize( s_header.uTracksCount, false );
		
		sAnim.fFrameRate	 = s_header.fFrameRate;
		sAnim.uFramesCount	 = s_header.uFramesCount;
		sAnim.uJointPerFrame = s_header.uJointPerFrame;

		b_read &= pFile->ReadS( a_joint_infos.ptr(),			a_joint_infos.Size() );
		b_read &= pFile->ReadS( sAnim.aJoints.ptr(),			sAnim.aJoints.Size() );
		b_read &= pFile->ReadS( sAnim.aBBoxes.ptr(),			sAnim.aBBoxes.Size() );
		b_read &= pFile->ReadS( sAnim.aTrackFrames.ptr(),	sAnim.aTrackFrames.Size() );
		b_read &= pFile->ReadS( a_track_anims.ptr(),			a_track_anims.Size() );

		for (uint i = 0; i < a_joint_infos.Count(); ++i)
		{
			a_joint_infos[i].aName[ COUNT_OF( a_joint_infos[i].aName )-1 ] = 0;
			sAnim.aJointInfos[i].sName		= a_joint_infos[i].aName;
			sAnim.aJointInfos[i].uGroupBits	= a_joint_infos[i].uGroupBits;
			sAnim.aJointInfos[i].uParent	= a_joint_infos[i].uParent;
		}

		for (uint i = 0; i < a_track_anims.Count(); ++i)
		{
			a_track_anims[i].aName[ COUNT_OF( a_track_anims[i].aName )-1 ] = 0;
			sAnim.aTracks[i].sName			= a_track_anims[i].aName;
			sAnim.aTracks[i].uFirstFrame	= a_track_anims[i].uFirstFrame;
			sAnim.aTracks[i].uFramesCount	= a_track_anims[i].uFramesCount;
			sAnim.aTracks[i].uGroupMask		= a_track_anims[i].uGroupMask;
		}

		return b_read;
	}


	bool LoadMaterial(IFilePtr pFile, TMeshMaterial &sMaterial, e_model_component::type &eType)
	{
		char	a_mtr_type[4];
		bool	b_read = true;

		b_read &= pFile->ReadS( a_mtr_type, sizeof(a_mtr_type) );
		a_mtr_type[3] = '\0';

		if ( strcmp( a_mtr_type, "MTL" ) == 0 )
		{
			b_read &= sMaterial.Create( TMeshMaterial::MTR_LIST );
			b_read &= LoadMaterialsList( pFile, *sMaterial.sData.pMtrList );
			eType  |= e_model_component::MATERIAL_LIST;
		}
		
		else
			b_read = false;

		return b_read;
	}
	

	bool LoadMaterialsList(IFilePtr pFile, TMeshMaterialsList &sMtrs)
	{
		bool	b_read	= true;
		uint32	temp	= 0;

		b_read &= pFile->ReadS( &temp, sizeof(temp) );
		sMtrs.aMaterials.Resize( temp, false );

		for (uint i = 0; i < sMtrs.aMaterials.Count(); ++i)
		{
			TMeshMaterialsList::TSubMeshMaterial &	s_sm_mtr = sMtrs.aMaterials[i];

			b_read &= pFile->ReadS( &temp, sizeof(temp) );
			s_sm_mtr.aMeshIndices.Resize( temp, false );

			b_read &= pFile->ReadS( &temp, sizeof(temp) );
			s_sm_mtr.sFileName.Reserve( temp );

			b_read &= pFile->ReadS( s_sm_mtr.aMeshIndices.ptr(), s_sm_mtr.aMeshIndices.Size() );
			b_read &= pFile->ReadS( s_sm_mtr.sFileName.ptr(),	 temp );
			s_sm_mtr.sFileName.SetLength( temp-1 );
		}

		return b_read;
	}


	bool LoadPhysics(IFilePtr pFile, TMeshPhysics &sPhysics, e_model_component::type &eType)
	{
		return false;
	}

	
	bool SaveModel(IFilePtr pFile, const TMesh *pMesh, const TMeshAnimation *pAnimation,
					const TMeshMaterial *pMaterial, const TMeshPhysics *pPhysics)
	{
		CHECK_ARGUMENT( pFile != nullptr );
		CHECK_ARGUMENT( pMesh != nullptr || pAnimation != nullptr ||
						pMaterial != nullptr || pPhysics != nullptr );

		TModelHeader	s_header;
		TFileHeader		s_file_header;
		uint			u_pos		= pFile->GetPos(),
						u_hd_pos	= 0;
		bool			b_write		= true;

		s_file_header.uHeaderSize	= sizeof(s_header);
		s_file_header.uUserDataSize	= 0;

		s_header.uGeometryOffset	= 0;
		s_header.uAnimationOffset	= 0;
		s_header.uMaterialOffset	= 0;
		s_header.uPhysicsOffset		= 0;

		b_write &= pFile->WriteS( s_aMdlFileHeader, 12 );
		b_write &= pFile->WriteS( &s_file_header, sizeof(s_file_header) );
	
		u_hd_pos = pFile->GetPos();
		b_write &= pFile->WriteS( &s_header, sizeof(s_header) );
		

		if ( pMesh != nullptr )
		{
			s_header.uGeometryOffset = pFile->GetPos() - u_pos;
			b_write &= SaveMesh( pFile, *pMesh );
		}

		if ( pAnimation != nullptr )
		{
			s_header.uAnimationOffset = pFile->GetPos() - u_pos;
			b_write &= SaveAnimation( pFile, *pAnimation );
		}

		if ( pMaterial != nullptr )
		{
			s_header.uMaterialOffset = pFile->GetPos() - u_pos;
			b_write &= SaveMaterial( pFile, *pMaterial );
		}

		if ( pPhysics != nullptr )
		{
			s_header.uPhysicsOffset = pFile->GetPos() - u_pos;
			b_write &= SavePhysics( pFile, *pPhysics );
		}

		// update offsets //
		b_write &= pFile->SeekS( u_hd_pos, e_seek::SET );
		b_write &= pFile->WriteS( &s_header, sizeof(s_header) );

		return b_write;
	}

	
	bool SaveMesh(IFilePtr pFile, const TMesh &sMesh)
	{
		switch ( sMesh.eType )
		{
			case TMesh::MULTI_MESH :
				if ( sMesh.sData.pMesh->aSubMeshes.Count() == 1 )
					return SaveSingleMesh( pFile, *sMesh.sData.pMesh );
				else
					return SaveMultiMesh( pFile, *sMesh.sData.pMesh );
		
			case TMesh::SCELETAL_MESH :
				return SaveSceletalMesh( pFile, *sMesh.sData.pScelet );
			
			default :
				return false;
		}
	}
	

	bool SaveSingleMesh(IFilePtr pFile, const TMultiMesh &sMesh)
	{
		const char		a_mesh_type[4] = "SMH";
		TSMeshHeader	s_mesh_header;
		bool			b_write = true;

		s_mesh_header.eMode				= sMesh.aSubMeshes.Front().eMode;
		s_mesh_header.sAABB				= sMesh.aSubMeshes.Front().sBBox;
		s_mesh_header.uIdxSize			= GetIndexSize( sMesh.aSubMeshes.Front().eType );
		s_mesh_header.uIndicesSize		= sMesh.aIndices.Size();
		s_mesh_header.uVertAttribCount	= sMesh.aSubMeshes.Front().aAttribs.Count();
		s_mesh_header.uVerticesSize		= sMesh.aVertices.Size();
			
		sMesh.aSubMeshes.Front().sName.CopyTo( s_mesh_header.aName );

		b_write &= pFile->WriteS( a_mesh_type,		sizeof(a_mesh_type) );
		b_write &= pFile->WriteS( &s_mesh_header,	sizeof(s_mesh_header) );
		b_write &= pFile->WriteS( sMesh.aSubMeshes.Back().aAttribs.ptr(), sMesh.aSubMeshes.Back().aAttribs.Size() );
		b_write &= pFile->WriteS( sMesh.aIndices.ptr(),		sMesh.aIndices.Size() );
		b_write &= pFile->WriteS( sMesh.aVertices.ptr(),		sMesh.aVertices.Size() );
		
		return b_write;
	}


	bool SaveMultiMesh(IFilePtr pFile, const TMultiMesh &sMesh)
	{
		const char			a_mesh_type[4] = "MMH";
		TMultiMeshHeader	s_mm_header;
		bool				b_write = true;

		s_mm_header.uIndicesSize	= sMesh.aIndices.Size();
		s_mm_header.uVerticesSize	= sMesh.aVertices.Size();
		s_mm_header.uSubMeshesCount	= sMesh.aSubMeshes.Count();
		s_mm_header.uIndicesCount	= sMesh.aSMIndices.Count();
		s_mm_header.uLODsCount		= sMesh.aLODs.Count();

		b_write &= pFile->WriteS( a_mesh_type, sizeof(a_mesh_type) );
		b_write &= pFile->WriteS( &s_mm_header, sizeof(s_mm_header) );

		for (uint i = 0; i < sMesh.aSubMeshes.Count(); ++i)
		{
			TSubMeshHeader					s_sm_header;
			TMultiMesh::TSubMesh const &	s_sub_mesh  = sMesh.aSubMeshes[i];
			
			s_sub_mesh.sName.CopyTo( s_sm_header.aName );

			s_sm_header.uAttribsCount	= s_sub_mesh.aAttribs.Count();
			s_sm_header.ePrimitiveMode	= s_sub_mesh.eMode;
			s_sm_header.uIdxSize		= GetIndexSize( s_sub_mesh.eType );
			s_sm_header.sAABB			= s_sub_mesh.sBBox;
			s_sm_header.uIdxCount		= s_sub_mesh.uIdxCount;
			s_sm_header.uIdxOffset		= s_sub_mesh.uIdxOffset;
			
			b_write &= pFile->WriteS( &s_sm_header, sizeof(s_sm_header) );
			b_write &= pFile->WriteS( s_sub_mesh.aAttribs.ptr(), s_sub_mesh.aAttribs.Size() );
		}

		b_write &= pFile->WriteS( sMesh.aIndices.ptr(),		sMesh.aIndices.Size() );
		b_write &= pFile->WriteS( sMesh.aVertices.ptr(),		sMesh.aVertices.Size() );
		b_write &= pFile->WriteS( sMesh.aSMIndices.ptr(),	sMesh.aSMIndices.Size() );
		b_write &= pFile->WriteS( sMesh.aLODs.ptr(),			sMesh.aLODs.Size() );

		return b_write;
	}


	bool SaveSceletalMesh(IFilePtr pFile, const TSceletalMesh &sMesh)
	{
		const char				a_mesh_type[4] = "SCM";
		TScelMultiMeshHeader	s_header;
		bool					b_write = true;

		s_header.uIndicesSize		= sMesh.aIndices.Size();
		s_header.uJointsPerFrame	= sMesh.uJointsPerFrame;
		s_header.uSubMeshesCount	= sMesh.aSubMeshes.Count();
		s_header.uVerticesSize		= sMesh.aVertices.Size();
		s_header.uWeightsCount		= sMesh.aWeights.Count();
		s_header.uIndicesCount		= sMesh.aSMIndices.Count();
		s_header.uLODsCount			= sMesh.aLODs.Count();
		
		b_write &= pFile->WriteS( a_mesh_type, sizeof(a_mesh_type) );
		b_write &= pFile->WriteS( &s_header,   sizeof(s_header) );

		for (uint i = 0; i < sMesh.aSubMeshes.Count(); ++i)
		{
			TScelSubMeshHeader				s_sm_header;
			const TSceletalMesh::TSubMesh &	s_sub_mesh = sMesh.aSubMeshes[i];

			s_sm_header.ePrimitiveMode	= s_sub_mesh.eMode;
			s_sm_header.uAttribsCount	= (uint)s_sub_mesh.aAttribs.Count();
			s_sm_header.uIdxCount		= s_sub_mesh.uIdxCount;
			s_sm_header.uIdxOffset		= s_sub_mesh.uIdxOffset;
			s_sm_header.uIdxSize		= GetIndexSize( s_sub_mesh.eType );

			b_write &= pFile->WriteS( &s_sm_header, sizeof(s_sm_header) );
			b_write &= pFile->WriteS( s_sub_mesh.aAttribs.ptr(), s_sub_mesh.aAttribs.Size() );
		}

		b_write &= pFile->WriteS( sMesh.aWeights.ptr(),		sMesh.aWeights.Size() );
		b_write &= pFile->WriteS( sMesh.aIndices.ptr(),		sMesh.aIndices.Size() );
		b_write &= pFile->WriteS( sMesh.aVertices.ptr(),		sMesh.aVertices.Size() );
		b_write &= pFile->WriteS( sMesh.aSMIndices.ptr(),	sMesh.aSMIndices.Size() );
		b_write &= pFile->WriteS( sMesh.aLODs.ptr(),			sMesh.aLODs.Size() );

		return b_write;
	}


	bool SaveAnimation(IFilePtr pFile, const TMeshAnimation &sAnim)
	{
		switch ( sAnim.eType )
		{
			case TMeshAnimation::FRAME :
				return SaveFrameAnim( pFile, *sAnim.sData.pFrameAnim );
		
			case TMeshAnimation::SCELETAL :
				return SaveSceletalAnim( pFile, *sAnim.sData.pSceletal );
			
			default :
				return false;
		};
	}
	

	bool SaveFrameAnim(IFilePtr pFile, const TFrameAnimData &sAnim)
	{
		typedef array< TFrameAnimTrack, TFastCopyStrategy< TFrameAnimTrack > >	track_array_t;

		const char			a_anim_type[4] = "FRM";
		TFrameAnimHeader	s_header;
		bool				b_write  = true;
		track_array_t		a_tracks;

		s_header.fFrameRate		= sAnim.fFrameRate;
		s_header.uFramesCount	= (uint)sAnim.aTrackFrames.Count();
		s_header.uTracksCount	= (uint)sAnim.aTracks.Count();
		a_tracks.Resize( sAnim.aTracks.Count(), false );

		for (usize i = 0; i < sAnim.aTracks.Count(); ++i)
		{
			sAnim.aTracks[i].sName.CopyTo( a_tracks[i].aName );
			a_tracks[i].uCount		= sAnim.aTracks[i].uCount;
			a_tracks[i].uFirstFrame	= sAnim.aTracks[i].uFirstFrame;
			a_tracks[i].uTrackType	= sAnim.aTracks[i].uTrackType;
		}

		b_write &= pFile->WriteS( a_anim_type,				sizeof(a_anim_type) );
		b_write &= pFile->WriteS( &s_header,					sizeof(s_header) );
		b_write &= pFile->WriteS( sAnim.aTrackFrames.ptr(),	sAnim.aTrackFrames.Size() );
		b_write &= pFile->WriteS( a_tracks.ptr(),			a_tracks.Size() );

		return b_write;
	}


	bool SaveSceletalAnim(IFilePtr pFile, const TSceletalAnimData &sAnim)
	{
		typedef array< TSAJointInfo, TFastCopyStrategy< TSAJointInfo > >	joint_info_array_t;
		typedef array< TSATrackInfo, TFastCopyStrategy< TSATrackInfo > >	track_anim_array_t;
		
		const char			a_anim_type[4] = "SCL";
		TSceletalAnimHeader	s_header;
		bool				b_write = true;
		joint_info_array_t	a_joint_infos;
		track_anim_array_t	a_track_anims;


		s_header.fFrameRate		= sAnim.fFrameRate;
		s_header.uFramesCount	= sAnim.uFramesCount;
		s_header.uJointPerFrame	= sAnim.uJointPerFrame;
		s_header.uTracksCount	= (uint)sAnim.aTracks.Count();
		s_header.uBBoxCount		= 0;

		a_joint_infos.Resize( s_header.uJointPerFrame, false );
		a_track_anims.Resize( s_header.uTracksCount, false );

		for (usize i = 0; i < sAnim.aTracks.Count(); ++i)
			s_header.uBBoxCount += sAnim.aTracks[i].uFramesCount;


		for (uint i = 0; i < sAnim.aJointInfos.Count(); ++i)
		{
			sAnim.aJointInfos[i].sName.CopyTo( a_joint_infos[i].aName );
			
			a_joint_infos[i].uGroupBits	= sAnim.aJointInfos[i].uGroupBits;
			a_joint_infos[i].uParent	= sAnim.aJointInfos[i].uParent;
		}

		for (uint i = 0; i < a_track_anims.Count(); ++i)
		{
			sAnim.aTracks[i].sName.CopyTo( a_track_anims[i].aName );

			a_track_anims[i].uFirstFrame	= sAnim.aTracks[i].uFirstFrame;
			a_track_anims[i].uFramesCount	= sAnim.aTracks[i].uFramesCount;
			a_track_anims[i].uGroupMask		= sAnim.aTracks[i].uGroupMask;
		}
		
		b_write &= pFile->WriteS( a_anim_type,				sizeof(a_anim_type) );
		b_write &= pFile->WriteS( &s_header,					sizeof(s_header) );
		b_write &= pFile->WriteS( a_joint_infos.ptr(),		a_joint_infos.Size() );
		b_write &= pFile->WriteS( sAnim.aJoints.ptr(),		sAnim.aJoints.Size() );
		b_write &= pFile->WriteS( sAnim.aBBoxes.ptr(),		sAnim.aBBoxes.Size() );
		b_write &= pFile->WriteS( sAnim.aTrackFrames.ptr(),	sAnim.aTrackFrames.Size() );
		b_write &= pFile->WriteS( a_track_anims.ptr(),		a_track_anims.Size() );

		return b_write;
	}


	bool SaveMaterial(IFilePtr pFile, const TMeshMaterial &sMaterial)
	{
		switch ( sMaterial.eType )
		{
			case TMeshMaterial::MTR_LIST :
				return SaveMaterialsList( pFile, *sMaterial.sData.pMtrList );

			default :
				return false;
		};
	}
	

	bool SaveMaterialsList(IFilePtr pFile, const TMeshMaterialsList &sMtrs)
	{
		bool		b_write			= true;
		uint32		temp			= 0;
		const char	a_mtr_type[4]	= "MTL";

		b_write &= pFile->WriteS( a_mtr_type, sizeof(a_mtr_type) );

		temp = (uint)sMtrs.aMaterials.Count();
		b_write &= pFile->WriteS( &temp, sizeof(temp) );
		
		for (uint i = 0; i < sMtrs.aMaterials.Count(); ++i)
		{
			const TMeshMaterialsList::TSubMeshMaterial &	s_sm_mtr = sMtrs.aMaterials[i];

			uint32	a_sizes[2] = {
						(uint)s_sm_mtr.aMeshIndices.Count(),
						(uint)s_sm_mtr.sFileName.Length()+1
			};

			b_write &= pFile->WriteS( a_sizes,						sizeof(a_sizes) );
			b_write &= pFile->WriteS( s_sm_mtr.aMeshIndices.ptr(),	s_sm_mtr.aMeshIndices.Size() );
			b_write &= pFile->WriteS( s_sm_mtr.sFileName.ptr(),		s_sm_mtr.sFileName.Size() );
		}

		return b_write;
	}


	bool SavePhysics(IFilePtr pFile, const TMeshPhysics &sPhysics)
	{
		return false;
	}


	bool GetModelComponents(IFilePtr pFile, e_model_component::type &eType)
	{
		CHECK_ARGUMENT( pFile != nullptr );

		eType = e_model_component::UNKNOWN;

		bool			b_read = true;
		char			a_file_header[12],
						a_block_type[4];
		TModelHeader	s_header;
		TFileHeader		s_file_header;
		uint			u_pos	= pFile->GetPos();


		b_read &= pFile->ReadS( a_file_header, sizeof(a_file_header) );
		a_file_header[11] = '\0';

		CHECK_VALUE( strcmp( a_file_header, s_aMdlFileHeader ) == 0 );
		
		b_read &= pFile->ReadS( &s_file_header, sizeof(s_file_header) );
		b_read &= pFile->ReadS( &s_header, sizeof(s_header) );

		CHECK_VALUE( s_file_header.uHeaderSize == sizeof(s_header) );
		
		if ( s_header.uGeometryOffset != 0 )
		{
			b_read &= pFile->SeekS( u_pos + s_header.uGeometryOffset, e_seek::SET );
			b_read &= pFile->ReadS( a_block_type, sizeof(a_block_type) );
			a_block_type[3] = '\0';

			if ( strcmp( a_block_type, "SMH" ) == 0 )
				eType |= e_model_component::MESH;
			else
			if ( strcmp( a_block_type, "MMH" ) == 0 )
				eType |= e_model_component::MULTI_MESH;
			else
			if ( strcmp( a_block_type, "SCM" ) == 0 )
				eType |= e_model_component::SCELETAL_MULTI_MESH;
			else
				eType |= e_model_component::_GEOMETRY_MASK;
		}

		if ( s_header.uAnimationOffset != 0 )
		{
			b_read &= pFile->SeekS( u_pos + s_header.uAnimationOffset, e_seek::SET );
			b_read &= pFile->ReadS( a_block_type, sizeof(a_block_type) );
			a_block_type[3] = '\0';

			if ( strcmp( a_block_type, "MOR" ) == 0 )
				eType |= e_model_component::MORPHING_ANIM;
			else
			if ( strcmp( a_block_type, "SCL" ) == 0 )
				eType |= e_model_component::SCELETAL_ANIM;
			else
			if ( strcmp( a_block_type, "TRF" ) == 0 )
				eType |= e_model_component::TRANSFORM_ANIM;
			else
			if ( strcmp( a_block_type, "FRM" ) == 0 )
				eType |= e_model_component::FRAME_ANIM;
			else
				eType |= e_model_component::_ANIMATION_MASK;
		}

		if ( s_header.uMaterialOffset != 0 )
		{
			b_read &= pFile->SeekS( u_pos + s_header.uMaterialOffset, e_seek::SET );
			b_read &= pFile->ReadS( a_block_type, sizeof(a_block_type) );
			a_block_type[3] = '\0';

			if ( strcmp( a_block_type, "MTR" ) == 0 )
				eType |= e_model_component::MATERIAL_LIST;
			else
				eType |= e_model_component::_MATERIAL_MASK;
		}

		if ( s_header.uPhysicsOffset != 0 )
		{
		}

		b_read &= pFile->SeekS( u_pos, e_seek::SET );

		return b_read;
	}

	
	bool GetMeshDataSize(IFilePtr pFile, uint &uVerticesSize, uint &uIndicesSize)
	{
		CHECK_ARGUMENT( pFile != nullptr );

		bool			b_read = true;
		char			a_file_header[12],
						a_block_type[4];
		TModelHeader	s_header;
		TFileHeader		s_file_header;
		uint			u_pos	= pFile->GetPos();


		b_read &= pFile->ReadS( a_file_header, sizeof(a_file_header) );
		a_file_header[11] = '\0';

		CHECK_VALUE( strcmp( a_file_header, s_aMdlFileHeader ) == 0 );
		
		b_read &= pFile->ReadS( &s_file_header, sizeof(s_file_header) );
		b_read &= pFile->ReadS( &s_header, sizeof(s_header) );

		CHECK_VALUE( s_file_header.uHeaderSize == sizeof(s_header) );

		if ( s_header.uGeometryOffset != 0 )
		{
			b_read &= pFile->SeekS( u_pos + s_header.uGeometryOffset, e_seek::SET );
			b_read &= pFile->ReadS( a_block_type, sizeof(a_block_type) );
			a_block_type[3] = '\0';

			if ( strcmp( a_block_type, "SMH" ) == 0 )
			{
				TSMeshHeader	s_mesh_header;

				b_read &= pFile->ReadS( &s_mesh_header, sizeof(s_mesh_header) );

				uVerticesSize = s_mesh_header.uVerticesSize;
				uIndicesSize  = s_mesh_header.uIndicesSize;
			}
			else
			
			if ( strcmp( a_block_type, "MMH" ) == 0 )
			{
				TMultiMeshHeader	s_mm_header;

				b_read &= pFile->ReadS( &s_mm_header, sizeof(s_mm_header) );

				uVerticesSize = s_mm_header.uVerticesSize;
				uIndicesSize  = s_mm_header.uIndicesSize;
			}
			else

			if ( strcmp( a_block_type, "SCM" ) == 0 )
			{
				TScelMultiMeshHeader	s_header;

				b_read &= pFile->ReadS( &s_header, sizeof(s_header) );

				uVerticesSize = s_header.uVerticesSize;
				uIndicesSize  = s_header.uIndicesSize;
			}

			else
				b_read = false;
		}

		b_read &= pFile->SeekS( u_pos, e_seek::SET );

		return b_read;
	}

//-------------------------------------------------------------------

}	// Loaders
}	// Engine
